package generatore;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Vector;
import linguaggio.AttributeType;
import linguaggio.Element;
import linguaggio.NonTerminale;
import linguaggio.Produzione;
import linguaggio.Token;

/**
 * La classe offre una serie di utility utili alla creazie dei vari process builder.
 * @author Giovanni Cammarata
 */
public class Utility {
    public static enum OStype {linux,windows,mac,solaris,defaultOS};
    
    
    /**
     * Ritorna l'enumeratore col tipo di OS scegliendo tra:
     * Linux/Unix;
     * Windows;
     * Mac;
     * Solaris.
     * @return OStype.
     */
    public static OStype selezionaOS() {
        String OS = java.lang.System.getProperty("os.name").toUpperCase();
        if(OS.indexOf("NUX")>=0 || OS.indexOf("NIX")>=0)
            return OStype.linux;
        else if(OS.indexOf("WIN")>=0)
                return OStype.windows;
             else if(OS.indexOf("MAC")>=0)
                    return OStype.mac;
             else if(OS.indexOf("SUNOS")>=0)
                    return OStype.solaris;
                  else
                    return OStype.defaultOS;
             
        }
    
    /**
     * Permette la creazione del process builder in base al sistema operativo.
     * @param command
     * @param OS
     * @return ProcessBuilder
     */
    public static ProcessBuilder pbYacc(String yaccFilename, Utility.OStype OS,String outputFolder) {
        String command = null;
        String arg1pb = null;
        String arg2pb = null;
        
        //System.out.println(command);
        
        if(OS == Utility.OStype.linux) {
            command = "./lib/yacc.linux -J -Jpackage="+outputFolder+" "+yaccFilename;
            arg1pb = "bash";
            arg2pb = "-c";
            
        }
        else if(OS == Utility.OStype.windows) {
            command = "lib\\yacc.exe -J -Jpackage="+outputFolder+" "+yaccFilename;
            
            System.out.println("Eseguo il comando: "+command);
            arg1pb = "cmd.exe";
            arg2pb = "/C";
        }
        
                
        //System.out.println("\n\nCOMANDO "+command);
        return new ProcessBuilder(arg1pb,arg2pb,command);
    }
    
    /**
     * Permette la creazione del process builder in base al sistema operativo.
     * @param command
     * @param OS
     * @return ProcessBuilder
     */
    public static ProcessBuilder pbLex(String lexFilename, Utility.OStype OS){
        
        String command = "java -jar lib/JFlex.jar "+lexFilename;
        
        String arg1pb = null;
        String arg2pb = null;
        
        if(OS == OStype.linux) {
            arg1pb = "bash";
            arg2pb = "-c";
            
        }
        else if(OS == OStype.windows) {
            arg1pb = "cmd.exe";
            arg2pb = "/C";

            System.out.println("Eseguo il comando: "+command);
        } 
        
        return new ProcessBuilder(arg1pb,arg2pb,command);
    }
    
    /**
     * Il metodo viene richiamato durate la costruzione del lexer in modo da poter assegnare il tipo di valore al token.
     * Permette quindi, in maniera trasparente per l'utente la specifica dei token all'interno del Lexer.
     * @param tk
     * @return il token creato.
     */
    public static String creaToken(Token tk,String parserValClassName) {
        String toReturn = "";
        
        
        if(tk.getThisTokenType() == linguaggio.AttributeType.tokenType.sval)
            toReturn ="yyparser.yylval = new "+parserValClassName+"(yytext());";
        else if(tk.getThisTokenType() == linguaggio.AttributeType.tokenType.ival)
            toReturn ="yyparser.yylval = new "+parserValClassName+"(Integer.parseInt(yytext()));";
        else if(tk.getThisTokenType() == linguaggio.AttributeType.tokenType.dval)
            toReturn ="yyparser.yylval = new "+parserValClassName+"(Double.parseDouble(yytext()));";
            else if(tk.getThisTokenType() == linguaggio.AttributeType.tokenType.obj)
                toReturn =tk.getTypeAttribute()+" oggetto=new "+tk.getTypeAttribute()+tk.getCode()+";\n\t\tyyparser.yylval = new "+parserValClassName+"(oggetto);";
                 else toReturn ="";
        
        
        if(tk.getThisTokenType() != linguaggio.AttributeType.tokenType.ignora && tk.getThisTokenType() != linguaggio.AttributeType.tokenType.niente )
            toReturn+="\n\t\tSystem.out.println(\"LETTO: \"+yytext());";
        
        return toReturn;
    }
    
    
    /**
     * Il metodo permette di aggiungere codice relativo ad una regola in modo da poter popolare
     * l'albero sintattico decorato.
     * La costruzione dell'albero avviene in maniera totalmente automatica senza la necessita' adi specificare alcun
     * codice aggiuntivo.
     * Utilizza delle espressioni regolari per trovare il path ed ottenere il giusto ordine.
     * @param nt
     * @param p
     * @param v
     * @return 
     */
    public static String addNodeTree(NonTerminale nt, Produzione p,LinkedList<Token> v) {
        boolean aggiungoAdAlbero = false;
        String rimozioneAttributi="";
        int posizione=0;          //posizione nell'albero della parte relativa alla semantica che nn coinvolge terminali/non terminali
        boolean aggiuntoAlberoDecorato = false;   //booleano che indica se ho gia aggiunto la parte che non riguarda terminali/non terminali all'albero
        
        String rigaAlbero = "\n\t\tVector<String> attributi = new Vector<>();";
        String rigaAlberoNonTerminaleSx = "";
        
        String toReturn = "";
        String regola = "";
        
        
        Vector<Integer> occorrenze = new Vector<>();
       
            int indexNew=0;
            
            while(indexNew>=0 && indexNew<p.getRegolaSemantica().length()) {
                indexNew = p.getRegolaSemantica().indexOf("$", indexNew);

                if(indexNew>=0) {
                    occorrenze.add(new Integer(indexNew));
                    indexNew++;
                }                
            }
            
            boolean attributoNonTermninaleSx = false;
            if(occorrenze.size()>=2) {
                if(occorrenze.get(0).intValue() == 0 && occorrenze.get(1).intValue() == 1) {
                    attributoNonTermninaleSx = true;
                    occorrenze.remove(0);
                    occorrenze.remove(0);
                }
            }

            
            
            System.out.println("REGOLA "+p.getRegolaSemantica());
            System.out.println("attributoNonTerminale: "+attributoNonTermninaleSx);
            System.out.println("TROVATI: "+occorrenze.size());
            for (int k =0; k< occorrenze.size(); k++)
                System.out.println("Indice "+k+": "+occorrenze.get(k));
                
            
            String regolaAlbero ="";
            
            LinkedList<String> parteDestraAlbero = new LinkedList<>();
            
            String[] uguale = (p.getRegolaSemantica().trim()).split("=");
            if(uguale.length>1) {
                
                
                //Considero la parte sinistra della semantica
                if(uguale[0].equals("$$")) {
                    regolaAlbero+=rigaAlberoNonTerminaleSx;
                }
                
                
                //System.out.println("UGUALE "+uguale[0]);
                //System.out.println("UGUALE "+uguale[1]);
                
                //Considero ora la parte destra della semantica
                System.out.println("PARTE DESTRA");
                System.out.println("REGOLA: "+p.getRegolaSemantica());
                Iterator it = occorrenze.iterator();
                while(it.hasNext()) {
                    int indice = ((Integer)it.next()).intValue();
                    String dx = p.getRegolaSemantica().substring(indice,indice+2);
                    System.out.println(dx);
                    parteDestraAlbero.add(dx);
                    
                }

                //Devo ora valutare eventuali operazioni che non coinvolgono terminali o non terminale con
                // i loro attributi
                rimozioneAttributi = uguale[1];
                
                it = parteDestraAlbero.iterator();
                while(it.hasNext()) {
                    String parDex = (String) it.next();
                    rimozioneAttributi = rimozioneAttributi.replace(parDex, "");
                    
                    
                    //parteDestraAlbero.add(dx);
                    
                }
                
                //rimozioneAttributi= rimozioneAttributi.substring(0, rimozioneAttributi.length()-1);
               
                
                //System.out.println("Indice ;"+rimozioneAttributi.indexOf(";"));
                rimozioneAttributi=rimozioneAttributi.substring(0, (rimozioneAttributi.length()-1));
                //System.out.println("RIMOZIONE "+rimozioneAttributi);
                
                int indicePosizioneParteDxAlbero = (p.getRegolaSemantica()).indexOf(rimozioneAttributi.trim());
                
                
                //System.out.println("Indice "+rimozioneAttributi+" "+indicePosizioneParteDxAlbero);
                
                
                //Aggiungo ora nel giusto ordine dell'albero
                posizione = 0;
                it = occorrenze.iterator();
                boolean aggiunto = false;
                rimozioneAttributi = rimozioneAttributi.trim();
                rimozioneAttributi = rimozioneAttributi.replace(" ", "");
                if(rimozioneAttributi.trim().length()>0) {
                    while(it.hasNext()) {
                        posizione++;
                        int indice = ((Integer)it.next()).intValue();
                        //System.out.println(posizione +" "+ indice+" "+ indicePosizioneParteDxAlbero);
                        if(indicePosizioneParteDxAlbero>=indice) {
                            parteDestraAlbero.add(posizione, rimozioneAttributi);
                            aggiunto = true;
                        }

                    }

                    //A questo punto verifico se devo aggiungere alla fine
                    if(!aggiunto)
                        parteDestraAlbero.add(rimozioneAttributi);
                }
                
                //Stampo il contenuto dell'albero
                //System.out.println("REGOLA "+p.getRegolaSemantica());
                //System.out.println("ALBERO FINALE");
                it = parteDestraAlbero.iterator();
                while(it.hasNext()) {
                    String dx = (String) it.next();
                    //System.out.println(dx);
                }
                
                
                //rigaAlbero+="\n\t\tattributi.add(new String(\""+rimozioneAttributi+"\"));";
                
            }
        
        
        if(nt.getThisTokenType() == linguaggio.AttributeType.tokenType.sval) {
            toReturn ="\t\tSystem.out.println($$);";
            rigaAlbero+="\n\t\tattributi.add(new String($$));";
            aggiungoAdAlbero = true;
        }
        else if(nt.getThisTokenType() == linguaggio.AttributeType.tokenType.ival)
            {
                toReturn ="\t\tSystem.out.println($$);";
                rigaAlbero+="\n\t\tattributi.add(new String(Integer.toString($$)));";
                rigaAlberoNonTerminaleSx="\n\t\tattributi.add(new String(Integer.toString($$)));";
                aggiungoAdAlbero = true;
            }
             else if(nt.getThisTokenType() == linguaggio.AttributeType.tokenType.dval) {
                    toReturn ="\t\tSystem.out.println(Double.toString($$));";
                    rigaAlbero+="\n\t\tattributi.add(new String(Double.toString($$)));";
                    rigaAlberoNonTerminaleSx="\n\t\tattributi.add(new String(Double.toString($$)));";
                    aggiungoAdAlbero = true;
             }
                  else   if(nt.getThisTokenType() == linguaggio.AttributeType.tokenType.obj) {
                            toReturn ="\t\tSystem.out.println((("+nt.getTypeAttribute()+")$$).toString());";
                            rigaAlbero+="\n\t\tattributi.add(new String(((("+nt.getTypeAttribute()+")$$).toString())));";
                            rigaAlberoNonTerminaleSx="\n\t\tattributi.add(new String(((("+nt.getTypeAttribute()+")$$).toString())));";
                            aggiungoAdAlbero = true;
                  }
                         else
                            toReturn = "";
        
    
            //regola = p.getRegolaSemantica();
            int i=1;
            Element e;
        
            Iterator itDx = p.getParteDestra().iterator();
            aggiuntoAlberoDecorato = false;
            int indiceAlbero = 0;
            while(itDx.hasNext()) {
                indiceAlbero++;
                if(posizione<indiceAlbero && aggiuntoAlberoDecorato == false) {
                    if(rimozioneAttributi.length()>0) {
                        rigaAlbero+="\n\t\tattributi.add(new String(\""+rimozioneAttributi+"\"));";
                        aggiuntoAlberoDecorato = true;
                    }
                    
                }
                e = (Element)itDx.next();
                
                    if(e.getThisTokenType() == AttributeType.tokenType.sval) {
                        regola+="\n\t\tSystem.out.println($"+i+");";
                        rigaAlbero+="\n\t\tattributi.add(new String($"+i+"));";
                    }
                    else if(e.getThisTokenType() == AttributeType.tokenType.dval) {
                        regola+="\n\t\tSystem.out.println(Double.toString($"+i+"));";
                            rigaAlbero+="\n\t\tattributi.add(new String(Double.toString($"+i+")));";
                    }
                        else if(e.getThisTokenType() == AttributeType.tokenType.ival) {
                            regola+="\n\t\tSystem.out.println(Integer.toString($"+i+"));";
                            rigaAlbero+="\n\t\tattributi.add(new String(Integer.toString($"+i+")));";
                        }
                            else if(e.getThisTokenType() == AttributeType.tokenType.obj) {
                                    regola+="\n\t\tSystem.out.println((("+e.getTypeAttribute()+")$"+i+").toString());";
                                    rigaAlbero+="\n\t\tattributi.add(new String(((("+e.getTypeAttribute()+")$"+i+").toString())));";
                                    }
                
                    //System.out.print("\nNON TERMINALE: "+nt.toString()+" "+e.toString()+" "+regola);
                
                i++;
            }
       

            if(aggiungoAdAlbero && p.getParteDestra().size()>=2)
                //return toReturn+regola+rigaAlbero+";\n\t\tthis.addNode(attributi);";
                return toReturn+rigaAlbero+";\n\t\tthis.addNode(attributi);";
            else
                //return toReturn+regola+rigaAlbero+";";
                //return toReturn+rigaAlbero;
                return toReturn;
    }
    
    /**
     * Il metodo permette di aggiungere codice relativo ad una regola in modo da poter popolare
     * l'albero sintattico decorato.
     * La costruzione dell'albero avviene in maniera totalmente automatica senza la necessita' adi specificare alcun
     * codice aggiuntivo.
     * Utilizza delle espressioni regolari per trovare il path ed ottenere il giusto ordine.
     * @param nt
     * @param p
     * @param v
     * @return 
     */
    public static String addNodeTree2(NonTerminale nt, Produzione p,LinkedList<Token> v) {
        boolean aggiungoAdAlbero = false;
        String rimozioneAttributi="";
        int posizione=0;          //posizione nell'albero della parte relativa alla semantica che nn coinvolge terminali/non terminali
        boolean aggiuntoAlberoDecorato = false;   //booleano che indica se ho gia aggiunto la parte che non riguarda terminali/non terminali all'albero
        
        //String rigaAlbero = "\n\t\tVector<String> attributi = new Vector<>();";
        String rigaAlbero = "";
        String rigaAlberoNonTerminaleSx = "";
        
        String toReturn = "";
        String regola = "";
        
        if(nt.getThisTokenType() == linguaggio.AttributeType.tokenType.sval) {
            //toReturn ="\t\tSystem.out.println($$);";
            //rigaAlbero+="\n\t\tattributi.add(new String($$));";
            rigaAlbero+="\n\t\tNodoASD root = new NodoASD(new String($$),\""+nt.getName()+"\");";
            aggiungoAdAlbero = true;
        }
        else if(nt.getThisTokenType() == linguaggio.AttributeType.tokenType.ival)
            {
                //toReturn ="\t\tSystem.out.println($$);";
                //rigaAlbero+="\n\t\tattributi.add(new String(Integer.toString($$)));";
                rigaAlberoNonTerminaleSx="\n\t\tattributi.add(new String(Integer.toString($$)));";
                rigaAlbero+="\n\t\tNodoASD root = new NodoASD(new String(Integer.toString($$)),\""+nt.getName()+"\");";
                aggiungoAdAlbero = true;
            }
             else if(nt.getThisTokenType() == linguaggio.AttributeType.tokenType.dval) {
                    //toReturn ="\t\tSystem.out.println(Double.toString($$));";
                    //rigaAlbero+="\n\t\tattributi.add(new String(Double.toString($$)));";
                    rigaAlbero+="\n\t\tNodoASD root = new NodoASD(new String(Double.toString($$)),\""+nt.getName()+"\");";
                    rigaAlberoNonTerminaleSx="\n\t\tattributi.add(new String(Double.toString($$)));";
                    aggiungoAdAlbero = true;
             }
                  else   if(nt.getThisTokenType() == linguaggio.AttributeType.tokenType.obj) {
                            //toReturn ="\t\tSystem.out.println((("+nt.getTypeAttribute()+")$$).toString());";
                            //rigaAlbero+="\n\t\tattributi.add(new String(((("+nt.getTypeAttribute()+")$$).toString())));";
                            rigaAlbero+="\n\t\tNodoASD root = new NodoASD(new String(((("+nt.getTypeAttribute()+")$$))),\""+nt.getName()+"\");";
                            rigaAlberoNonTerminaleSx="\n\t\tattributi.add(new String(((("+nt.getTypeAttribute()+")$$).toString())));";
                            aggiungoAdAlbero = true;
                  }
                         else
                            toReturn = "";
        
    
            int lunghezzaSemantica = p.getRegolaSemantica().length();
            if(lunghezzaSemantica<1)
                return null;
            
           
            Element e;
            String carattere;
            String carattere2;
            
            String[] tokens = p.getRegolaSemantica().split("=");
            String parteDestraSemantica = tokens[tokens.length-1];
            lunghezzaSemantica = parteDestraSemantica.length();
            String nodoAsd="";
            System.out.println("\n\nDESTRA: "+parteDestraSemantica);
            for(int i=0; i<lunghezzaSemantica; i++) {
                carattere = String.valueOf(parteDestraSemantica.charAt(i));
                System.out.println("\n\nCARATTERE:"+carattere);
                if(carattere.equals("$")){
                        
                        
                        
                        int index = Integer.parseInt(parteDestraSemantica.substring(i+1,i+2));
                        String elementoNome = p.getParteDestra().get(index-1).getName();
                        e = p.getParteDestra().get(index-1);
                        if(e.getThisTokenType() == AttributeType.tokenType.sval) {
                            regola+="\n\t\tSystem.out.println($"+index+");";
                            //rigaAlbero+="\n\t\tattributi.add(new String($"+index+"));";
                            rigaAlbero+="\n\t\troot.aggiungiFiglio(new NodoASD($"+index+",\""+elementoNome+"\"));";
                        }
                        else if(e.getThisTokenType() == AttributeType.tokenType.dval) {
                            regola+="\n\t\tSystem.out.println(Double.toString($"+index+"));";
                               // rigaAlbero+="\n\t\tattributi.add(new String(Double.toString($"+index+")));";
                                rigaAlbero+="\n\t\troot.aggiungiFiglio(new NodoASD(Double.toString($"+index+"),\""+elementoNome+"\"));";
                        }
                            else if(e.getThisTokenType() == AttributeType.tokenType.ival) {
                                regola+="\n\t\tSystem.out.println(Integer.toString($"+index+"));";
                                //rigaAlbero+="\n\t\tattributi.add(new String(Integer.toString($"+index+")));";
                                rigaAlbero+="\n\t\troot.aggiungiFiglio(new NodoASD(Integer.toString($"+index+"),\""+elementoNome+"\"));";
                            }
                                else if(e.getThisTokenType() == AttributeType.tokenType.obj) {
                                        regola+="\n\t\tSystem.out.println((("+e.getTypeAttribute()+")$"+index+").toString());";
                                        //rigaAlbero+="\n\t\tattributi.add(new String(((("+e.getTypeAttribute()+")$"+index+").toString())));";
                                        rigaAlbero+="\n\t\troot.aggiungiFiglio(new NodoASD(((("+e.getTypeAttribute()+")$"+index+").toString()),\""+elementoNome+"\"));";
                                     }
                        i++;
                }
                else if(!carattere.equals(" ")){
                    nodoAsd="";
                    carattere2=carattere;
                    System.out.println("\n\nINDICE: "+i);
                    while( i<(lunghezzaSemantica-1)) {
                        if(carattere2.equals("$")) {
                            i--;
                            break;
                        }
                            
                        nodoAsd+=String.valueOf(carattere2);
                        i++;
                        carattere2 = String.valueOf(parteDestraSemantica.charAt(i));
                    }
                    
                    nodoAsd=nodoAsd.trim();
                    if(nodoAsd.length()>0) {
                        //rigaAlbero+="\n\t\tattributi.add(new String(\""+nodoAsd+"\"));";
                        rigaAlbero+="\n\t\troot.aggiungiFiglio(new NodoASD(\""+nodoAsd+"\"));";
                    }        
                }           
                
                        
            }
     

            if( p.getParteDestra().size()>=1 )
                //return toReturn+regola+rigaAlbero+";\n\t\tthis.addNode(attributi);";
                //return toReturn+rigaAlbero+";\n\t\tthis.addNode(attributi);"+"\n\t\tthis.addNodoASD(root);";
                return toReturn+rigaAlbero+"\n\t\tthis.addNodoASD(root);";
            else
                //return toReturn+regola+rigaAlbero+";";
                //return toReturn+rigaAlbero;
                return toReturn;
    }
    
    
    
     /**
     * Il metodo permette di aggiungere codice relativo ad una regola in modo da poter popolare
     * l'albero sintattico decorato.
     * La costruzione dell'albero avviene in maniera totalmente automatica senza la necessita' adi specificare alcun
     * codice aggiuntivo.
     * Utilizza delle espressioni regolari per trovare il path ed ottenere il giusto ordine.
     * @param nt
     * @param p
     * @param v
     * @return 
     */
    public static String addNodeASD(NonTerminale nt, Produzione p,LinkedList<Token> v) {
        boolean aggiungoAdAlbero = false;
        String rimozioneAttributi="";
        int posizione=0;          //posizione nell'albero della parte relativa alla semantica che nn coinvolge terminali/non terminali
        boolean aggiuntoAlberoDecorato = false;   //booleano che indica se ho gia aggiunto la parte che non riguarda terminali/non terminali all'albero
        
        String rigaAlbero = "\n\t\tVector<String> attributi = new Vector<>();";
        String rigaAlberoNonTerminaleSx = "";
        
        String toReturn = "";
        String regola = "";
        
       
            String regolaAlbero ="";
            
            LinkedList<String> parteDestraAlbero = new LinkedList<>();
            
           int lunghezzaRegolaSemantica = p.getRegolaSemantica().length();
           
           if(lunghezzaRegolaSemantica<1)
               return null;
        
           
           String rigaCostruzioneASD="";
    
            //regola = p.getRegolaSemantica();
            int i=1;
            Element e;
        
            Iterator itDx = p.getParteDestra().iterator();
            aggiuntoAlberoDecorato = false;
            int indiceAlbero = 0;
            while(itDx.hasNext()) {
                indiceAlbero++;
                if(posizione<indiceAlbero && aggiuntoAlberoDecorato == false) {
                    if(rimozioneAttributi.length()>0) {
                        rigaAlbero+="\n\t\tattributi.add(new String(\""+rimozioneAttributi+"\"));";
                        aggiuntoAlberoDecorato = true;
                    }
                    
                }
                e = (Element)itDx.next();
                
                    if(e.getThisTokenType() == AttributeType.tokenType.sval) {
                        regola+="\n\t\tSystem.out.println($"+i+");";
                        rigaAlbero+="\n\t\tattributi.add(new String($"+i+"));";
                    }
                    else if(e.getThisTokenType() == AttributeType.tokenType.dval) {
                        regola+="\n\t\tSystem.out.println(Double.toString($"+i+"));";
                            rigaAlbero+="\n\t\tattributi.add(new String(Double.toString($"+i+")));";
                    }
                        else if(e.getThisTokenType() == AttributeType.tokenType.ival) {
                            regola+="\n\t\tSystem.out.println(Integer.toString($"+i+"));";
                            rigaAlbero+="\n\t\tattributi.add(new String(Integer.toString($"+i+")));";
                        }
                            else if(e.getThisTokenType() == AttributeType.tokenType.obj) {
                                    regola+="\n\t\tSystem.out.println((("+e.getTypeAttribute()+")$"+i+").toString());";
                                    rigaAlbero+="\n\t\tattributi.add(new String(((("+e.getTypeAttribute()+")$"+i+").toString())));";
                                    }
                
                    //System.out.print("\nNON TERMINALE: "+nt.toString()+" "+e.toString()+" "+regola);
                
                i++;
            }
       

            if(aggiungoAdAlbero && p.getParteDestra().size()>=2)
                //return toReturn+regola+rigaAlbero+";\n\t\tthis.addNode(attributi);";
                return toReturn+rigaAlbero+";\n\t\tthis.addNode(attributi);";
            else
                //return toReturn+regola+rigaAlbero+";";
                //return toReturn+rigaAlbero;
                return toReturn;
    }
        
}
